
/*
 * @项目名称: 模板开发
 * @FilePath: /meek/src/components/select.js
 * @Author: 1106408264@qq.com
 * @Date: 2017-05-24T09:21:16+08:00
 * @LastEditors: yangxianghong@datatang.com
 * @LastEditTime: 2023-08-03 18:10:00
 * Copyright (c) 2018-2022 
 */
                                                    
/**
 * Created by zhangyong on 2017/5/23.
 */

import Component from './component'
import BrowserEvent from '../meek/browserevent'
import FeatureLayer from '../lyr/featurelayer'
import { listen, unlistenByKey } from '../core/eventmanager'
import { EventType } from '../meek/eventtype'
import { Style } from '../style/style'
import SelectEvent from '../components/selectevent'
import Geometry from '../geometry/geometry'
import { cloneHoleToFeature, getInnerHoles } from '../components/utils/selecthole'
import Draw from 'components/draw';
import DrawEvent from 'components/drawevent';
import intersect from './utils/intersect'

let hasMutilSelectedSymbol = Symbol('hasmutilselected')
/**
 * 图形选择模式
 *
 * @class Select
 * @extends Component
 * @module component
 * @constructor
 * @example
 *
 *      // 实例化选择工具
 *      selectTool = new Datatang.Select({
 *       selectMode: Datatang.BrowserEvent.CLICK,
 *       selectMultiMode: selectMultiMode
 *      });
 *
 *
 */
export default class Select extends Component {

  /**
   * 构造函数
   *
   * @constructor constructor
   * @param options
   */

  constructor(options = {}) {
    super()

    /**
     * 当前鼠标点与图形之间的公差距离，小于该值则可已选中图形
     *
     * @property hitTolerance
     * @type {number}
     * @private
     */
    this._hitTolerance = options.hitTolerance ? options.hitTolerance : 6

    /**
     * 选择图层是否配置了样式
     *
     * @type {boolean}
     * @private
       */
    this._hasSelectedStyle = false
    /**
     * 选择图层是否配置了样式
     *
     * @type {boolean}
     * @private
       */
    this._selectFeatureType = options.selectFeatureType || "all"

    /**
     * 多选模式，默认为false
     *
     * @property multiSelectMode
     * @type {boolean}
     * @private
     */
    this._multiSelectMode = false

    /**
     * 初始化草稿图层，用于临时高亮显示选中的图形
     *
     * @property selectLayer
     * @type {FeatureLayer}
     * @private
     */
    this._selectLayer = new FeatureLayer()

    if (options.style) {
      this._selectLayer.style = options.style
      this._hasSelectedStyle = true
    } else {
      this._hasSelectedStyle = false
    }
    /**
     * 选中图形的feature集合
     *
     * @property selectFeatures
     * @type {Array}
     * @private
     */
    this._selectFeatures = []
    this._selectHoles = []

    this._selectFeaturesHover = []
    this._selectHolesHover = []

    /**
     * 鼠标按下时的坐标点
     *
     * @property downPointPx
     * @type {null}
     * @private
     */
    this._downPointPx = null

    /**
     * 选中的模式，默认为单击选中
     *
     * @property selectMode
     * @type {string}
     */
    this.selectMode = options.selectMode ? options.selectMode : BrowserEvent.CLICK;
    this.dragSelectMode = options.dragSelectMode ? options.dragSelectMode : [];

    this._selectMultiMode = options.selectMultiMode ?
      options.selectMultiMode : function () { return true }
    //拖动选中需要碰撞检测的feature
    this._featureFilter = options.featureFilter ? options.featureFilter : null;
    //是否启用拖动选中
    this._dragSelectCondition = options.dragSelectCondition ? options.dragSelectCondition : () => false;
    /**
     * listen Ctrl key envet
     */
    if (options.selectMultiMode) {
      listen(document, 'keydown', this._handleCtrlKeyDwon, this)
    }
    if (options.selectMultiMode) {
      listen(document, 'keyup', this._handleCtrlKeyUp, this)
    }
  }

  get mapFeatures() {
    if (this._featureFilter) {
      return this._featureFilter();
    }
    if (this.map) {
      let f = [];
      this.map.layers.forEach(layer => {
        if (layer.name !== 'dragSelect') {
          if (layer.features && layer.features.length) {
            f = f.concat(layer.features)
          }
        }
      })
      return f;
    } else {
      return [];
    }
  }

  get dragSelect() {
    let data = null;
    this.dragSelectMode.some(item => {
      if (item === 'contain' || item === 'cover') {
        data = item;
        return true;
      }
      return false;
    })
    return data;
  }

  _condition(event) {
    return this.selectMode[0] === event.type || this.selectMode[1] === event.type
  }

  _keyPress() { }

  /**
   * handle Ctrl key down event
   *
   * 处理Ctrt键按下事件
   *
   * handleCtrlKeyDwon
   * @param event
   * @private
   */
  _handleCtrlKeyDwon(event) {
    if (this._selectMultiMode(event)) {
      this._multiSelectMode = true
    }
  }

  /**
   * handle Ctrl key up event
   *
   * 处理Ctrl键弹起事件
   *
   * @param event
   * @private
   */
  _handleCtrlKeyUp(event) {
    // Ctrl button down
    if (this._selectMultiMode(event)) {
      this._multiSelectMode = false
    }
  }
  initDragSelect() {
    if(this._drawTool) {
      return ;
    }
    const _this = this;
    this._flayer = new FeatureLayer({ name: 'dragSelect', select: false });
    this._drawTool = new Draw({
      drawLayer: this._flayer,
      snapTolerance: 8,
      displayExtentLwText: false,
      condition(e) {
        if (e.originalEvent.ctrlKey) {
          return false;
        }
        return _this._dragSelectCondition() && _this.dragSelect;
      }
    })
    this._drawTool.longPress = true;
    this._drawTool.drawMode = 'Extent';
    this.map.addComponents(this._drawTool);
    this.map.addLayer(this._flayer);
    this._drawTool.addEventListener(DrawEvent.EventType.DRAW_END, drawEvent => {
        let feature = drawEvent.feature;
        if (!feature) {
          return ;
        }
        if (!this.dragSelect) {
          return ;
        }
        this.clear();
        let selected = this.getSelectedFeature(feature, this.mapFeatures);

        
        if (selected.length) {
          this.selectFeatures = selected;
          this._forEachStyle()
          this._selectLayer.addFeatures(this.selectFeatures)
          this.dispatchEvent(
            new SelectEvent(SelectEvent.EventType.SELECT, this.selectFeatures, {
              originalEvent: {
                ctrlKey: true,
                button: 1
              }
            }))
        }
        
        
        this._flayer.clear();
      }
    );
  }
  getSelectedFeature(extent, features) {
    let selected = [];
    features.forEach(feature => {
      if (this._collisionDetection(extent, feature, this.dragSelect === 'contain')) {
        selected.push(feature);
      }
    })
    return selected;
  }
  /**
   * @description: 检测target(Extent)是否包含feature
   * @param {type} target: feature1, feature: feature2, fullInclude:是否完全包含
   * @return {type} 
   */
  _collisionDetection(target, feature, fullInclude) {
    const featureExtent = feature.geometry.extent;
    const featureCoords = featureExtent.getCoordinates();
    const targetExtent = target.geometry.extent;
    if (fullInclude) {
      return featureCoords.every(coord => {
        return target.geometry.extent.containsXY(coord[0], coord[1]);
      })
    } else {
      try {
        return intersect(target, feature);
      } catch (error) {
        return false;
      }
    }
  }
  /**
   * Handler mouse event
   *
   * 处理鼠标事件
   * handleMouseEvent
   * @method
   * @param browserEvent {browserEvent}
   */
  handleMouseEvent(browserEvent) {
    if(browserEvent.type === 'dblclick') {
      // 触发双击事件
      this.dispatchEvent(new SelectEvent(BrowserEvent.DBLCLICK));
      return true;
    }
    if (!this._condition(browserEvent)) {
      return true
    }
    if (browserEvent.type === 'mousemove') {
      return this.handleMouseMove(browserEvent);
    }

    const map = browserEvent.map
    const pixel = browserEvent.pixel
    const hitTolerance = this._hitTolerance

    const holeFeatures = []
    const holeParentFeatures = [];
    this.selectFeatures.forEach(feature => {
      if (!feature) {
        return;
      }
  
      let geometry = feature.geometry;
      if ( geometry.geometryType === Geometry.POLYGON ) {
        if (geometry.rings.length > 0) {
          let holeRings = geometry.getHoleAtCoord(browserEvent.coordinate);
          if ( holeRings.length > 0 ) {
            if (holeRings.length > 1) {
              // 判断选中的环是否有包含关系，如果有包含关系选中其中较小的环
              holeRings = getInnerHoles(holeRings)
            }
            let holeFeature = cloneHoleToFeature(feature, holeRings);
            holeFeatures.push(holeFeature);
            holeParentFeatures.push(feature);
          }
        }
      }
    })

    if (!this._multiSelectMode) {
      this.clear()
    }

    if (holeFeatures.length === 0) {
      this.clearHole()
    } else {
      this.selectHoles = holeFeatures
    }

    map.forEachFeatureAtPiexl(pixel, (features) => {
      holeParentFeatures.forEach(feature => {
        features.push(feature)
      })
      features = features.filter(feature => !feature.get("parent"))

      if (features.length > 0) {
        // 赋值并填充到selectFeatures中
        const isSelected = features.some(feature => this._isInSelectFeatures(feature));
        if (this._multiSelectMode && isSelected) { //ctrl取消选中
          const selected = this.selectFeatures.filter(item => {
            return !features.find(f => f.id === item.id);
          })
          this.clear();
          this.selectFeatures = selected;
        } else {
          if (this._selectFeatureType === "all") {
            this.selectFeatures = features
  
          } else {
            this.selectFeatures = features.filter(feature => {
              return feature.geometry.geometryType === this._selectFeatureType;
            })
          }
        }
        
      }
    }, hitTolerance)

    if ( this.selectFeatures.length === 0 && holeParentFeatures.length > 0 ) {
      this.selectFeatures = holeParentFeatures
    }

    if (this.selectFeatures.length > 0) {
      this._forEachStyle()
      this._selectLayer.addFeatures(this.selectFeatures)
    }

    // 派发要素的选择事件
    this.dispatchEvent(
      new SelectEvent(SelectEvent.EventType.SELECT, this.selectFeatures, browserEvent))

    if (this.selectHoles.length > 0) {
      this._selectLayer.addFeatures(this.selectHoles)
      this.dispatchEvent(
        new SelectEvent(SelectEvent.EventType.SELECT_HOLE, this.selectHoles, browserEvent))
    }
  }

  handleMouseMove(browserEvent) {
    const map = browserEvent.map
    const pixel = browserEvent.pixel
    const hitTolerance = this._hitTolerance

    const holeFeatures = []
    const holeParentFeatures = [];

    const clear = () => {
      this.selectFeaturesHover.forEach(feature => {
        if (!this._isInSelectFeatures(feature)) {
          feature.styleHighLightHover = false
          feature.delete(hasMutilSelectedSymbol)
        }
      })
      this.selectFeaturesHover = [];
      this.selectHolesHover = [];
    }
    this.selectFeaturesHover.forEach(feature => {
      if (!feature) {
        return;
      }
  
      let geometry = feature.geometry;
      if ( geometry.geometryType === Geometry.POLYGON ) {
        if (geometry.rings.length > 0) {
          let holeRings = geometry.getHoleAtCoord(browserEvent.coordinate);
          if ( holeRings.length > 0 ) {
            let holeFeature = cloneHoleToFeature(feature, holeRings);
            holeFeatures.push(holeFeature);
            holeParentFeatures.push(feature);
          }
        }
      }
    })
    clear();
    if (holeFeatures.length === 0) {
      this.selectHolesHover = [];
    } else {
      this.selectHolesHover = holeFeatures
    }

    map.forEachFeatureAtPiexl(pixel, (features) => {
      holeParentFeatures.forEach(feature => {
        features.push(feature)
      })
      features = features.filter(feature => {
        if (this._isInSelectFeatures(feature)) {
          return false;
        }
        return !feature.get("parent");
      })

      if (features.length > 0) {
        if (this._selectFeatureType === "all") {
          this.selectFeaturesHover = features
        } else {
          this.selectFeaturesHover = features.filter(feature => {
            return feature.geometry.geometryType === this._selectFeatureType;
          })
        }
      }
    }, hitTolerance)

    if ( this.selectFeaturesHover.length === 0 && holeParentFeatures.length > 0 ) {
      this.selectFeaturesHover = holeParentFeatures
    }

    if (this.selectFeaturesHover.length > 0) {
      if (!this._hasSelectedStyle) {
        const features = this.selectFeaturesHover
        features.forEach(feature => {
          feature.styleHighLight = false;
          feature.styleHighLightHover = true
        })
      }
      // this._selectLayer.addFeatures(this.selectFeaturesHover)
    }

    // 派发要素的选择事件
    this.dispatchEvent(
      new SelectEvent(SelectEvent.EventType.SELECT, this.selectFeaturesHover, browserEvent))

    /*
    if (this.selectHoles.length > 0) {
      this.dispatchEvent(
        new SelectEvent(SelectEvent.EventType.SELECT_HOLE, this.selectHolesHover, browserEvent))
    }
    */
  }

  /**
   * 清空选择的集合
   *
   * @method clear
   */
  clear() {
    this._selectLayer.clear()

    this.selectFeatures.forEach(feature => {
      feature.styleHighLightHover = false
      feature.styleHighLight = false
      feature.delete(hasMutilSelectedSymbol)
    })
    this.selectFeatures = []
    this.selectHoles = []
    this.dispatchEvent(
      new SelectEvent(SelectEvent.EventType.CLEAR, this.selectFeatures))
  }

  /**
   * 清空选择的内环集合
   *
   * @method clearHole
   */
  clearHole() {
    this.selectHoles.forEach(feature => {
      this._selectLayer.removeFeature(feature)
    })
    this.selectHoles = []
    this.dispatchEvent(
      new SelectEvent(SelectEvent.EventType.CLEAR, this.selectFeatures))
  }

  /**
   * 当前选中feature的集合的读写器
   *
   * @property selectFeatures
   * @type {Array}
   */
  get selectFeatures() { return this._selectFeatures }
  set selectFeatures(features) {
    if (features.length === 0) {
      this._selectFeatures = []
    } else {
      features.forEach(feature => {
        // 去掉重复的
        if (!this._isInSelectFeatures(feature)) {
          this._selectFeatures.push(feature)
        }
      })
    }
  }
  get selectFeaturesHover() { return this._selectFeaturesHover }
  set selectFeaturesHover(features) {
    if (features.length === 0) {
      this._selectFeaturesHover = []
    } else {
      features.forEach(feature => {
        // 去掉重复的
        if (!this._isInSelectFeaturesHover(feature)) {
          this._selectFeaturesHover.push(feature)
        }
      })
    }
  }
  /**
   * 当前选中feature图形的内环的集合的读写器
   *
   * @property selectHoles
   * @type {Array}
   */
  get selectHoles() { return this._selectHoles }
  set selectHoles(features) {
    if (features.length === 0) {
      this._selectHoles = []
    } else {
      features.forEach(feature => {
        const coord1 = feature.geometry.rings
        // 去掉重复的
        const result = this._selectHoles.some(hole => {
          const coord2 = hole.geometry.rings
          return coord1.join(",") === coord2.join(",")
        })
        if (!result) {
          this._selectHoles.push(feature)
        }
      })
    }
  }

  get selectHolesHover() { return this._selectHolesHover }
  set selectHolesHover(features) {
    if (features.length === 0) {
      this._selectHolesHover = []
    } else {
      features.forEach(feature => {
        const coord1 = feature.geometry.rings
        // 去掉重复的
        const result = this._selectHolesHover.some(hole => {
          const coord2 = hole.geometry.rings
          return coord1.join(",") === coord2.join(",")
        })
        if (!result) {
          this._selectHolesHover.push(feature)
        }
      })
    }
  }

  /**
   * 判断当前选中的集合中是否包含指定的feature
   *
   * @param feature
   * @returns {Boolean}
   * @private
   */
  _isInSelectFeatures(feature) {
    const features = this.selectFeatures
    const result = features.find(f => f.id === feature.id)

    return result === undefined ? false : true
  }
  _isInSelectFeaturesHover(feature) {
    const features = this.selectFeaturesHover
    const result = features.find(f => f.id === feature.id)

    return result === undefined ? false : true
  }
  

  /**
   * Update the drawing state for aborting drawing if active is false
   *
   * updateState
   * @private
   */
  _updateState() {
    const map = this.map
    const active = this.active
    if (!map || !active) {
      // this._abortDrawing()
    }

    map.addLayer(this._selectLayer)
  }

  /**
   * 高亮显示选中图形
   *
   * @method forEachStyle
   * @private
   */
  _forEachStyle() {
    if (this._hasSelectedStyle) {
      return
    }

    const features = this.selectFeatures
    features.forEach(feature => {
      if (!feature.get(hasMutilSelectedSymbol)) {
        feature.styleHighLightHover = false
        feature.set(hasMutilSelectedSymbol, true)
        feature.styleHighLight = true
      }
    })
  }

  /**
   * map读写器, 读取设置当前map
   *
   * @type {Function}
   * @property map
   * @param map {Object} Datatang.map
   */
  get map() { return this._map }
  set map(map) {
    if (this._mapRenderKey) {
      unlistenByKey(this._mapRenderKey)
      this._mapRenderKey = null
    }

    if (map) {
      const catchMap = this._map;
      this._map = map
      this._mapRenderKey = listen(this, EventType.CHANGE, map.render, map)
      if (map === catchMap) {
        this.initDragSelect();
      } else {
        this._drawTool = null;
        this.initDragSelect();
      }
    }

    this._updateState()
  }

  /**
   * 选择方式读写器
   *
   * @property selectMode
   * @type {Object} Datatang.BrowserEvent.CLICK
   * @return {*}
   */
  get selectMode() { return this._selectMode }
  set selectMode(value) {
    if (this._selectMode !== value) {
      this._selectMode = value
      this.clear()
    }
  }

  get dragSelectMode() {
    return this._dragSelectMode;
  }

  set dragSelectMode(value) {
    if (this._dragSelectMode !== value) {
      this._dragSelectMode = value
      this.clear()
    }
  }
  /**
   * 获取默认编辑样式
   *
   * @method getDefaultStyleFunction
   * @returns {Function}
   */
  getDefaultStyleFunction() {
    const styles = Style.createDefaultEditing()
    return feature => styles[feature.geometry.geometryType]
  }
}
